package org.feichao.xdao.util;

import org.apache.commons.lang.StringUtils;
import org.feichao.xdao.XDAO;
import org.feichao.xdao.util.parser.*;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SQL模版解析器，负责对SQL模版进行语法解析，并给出参数顺序等结果
 *
 * @author chao
 * @version 2015-05-26
 */
public class SQLTemplateParser {

	SQLContext context;

	public SQLTemplateParser(SQLContext context) {
		this.context = context;
	}

	public Result parse(){
		Result r =  new Result();
//        //处理:1、:personId 这样的参数，记住顺序后并转换成?
//		Pattern p = Pattern.compile(":\\w+");
//		Matcher m = p.matcher(context.sqlTemplate);
//		StringBuffer sb = new StringBuffer();
//		while (m.find()) {
//			r.paramOrder.add(StringUtils.substring(m.group(), 1));
//			m.appendReplacement(sb, "?");
//		}
//		m.appendTail(sb);
//        //TODO steven0lisa 更多的语法规则在这里添加
//		r.sql = sb.toString();

        //解析SQL模版
        List<Node> nodes = new ArrayList();

        String sqlTemplate = context.sqlTemplate;

        final int STATUS_TEXT = 0;
        final int STATUS_VAR = 1;
        final int STATUS_MACRO = 2;
        final int STATUS_METHOD = 3;
        final int STATUS_BATCH = 4;

        final char CHAR_VAR = '@';
        final char CHAR_MARCO = '$';
        final char CHAR_METHOD = '#';
        final char CHAR_BATCH = ':';

        int status = STATUS_TEXT;
        Node currentNode = new TextNode();

        for (int i = 0; i < sqlTemplate.length(); i++) { //开始解析SQL
            char c = sqlTemplate.charAt(i);

            if(c == CHAR_MARCO){//如果是Macro
                nodes.add(currentNode);
                currentNode = new MacroNode();
                status = STATUS_MACRO;
                continue;
            }else if(c == CHAR_VAR){//如果是Var
                nodes.add(currentNode);
                currentNode = new VarNode();
                status = STATUS_VAR;
                continue;
            }else if(c == CHAR_METHOD){//如果是Method
                nodes.add(currentNode);
                currentNode = new MethodNode();
                status = STATUS_METHOD;
                continue;
            }else if(c == CHAR_BATCH){//如果是Batch
                nodes.add(currentNode);
                currentNode = new BatchNode();
                status = STATUS_BATCH;
                continue;
            }
            switch (status){
                case STATUS_TEXT:
                    currentNode.content.append(c);
                    break;
                case STATUS_VAR:
                case STATUS_MACRO:
                    if((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || c == '_' || c == '.'){ // a-zA-Z0-9_.
                        currentNode.content.append(c);
                    }else{
                        nodes.add(currentNode);
                        currentNode = new TextNode();
                        currentNode.content.append(c);
                        status = STATUS_TEXT;
                    }
                    break;
                case STATUS_METHOD:
                    if((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || c == '_'){ // a-zA-Z0-9_
                        currentNode.content.append(c);
                    }else{
                        nodes.add(currentNode);
                        currentNode = new TextNode();
                        currentNode.content.append(c);
                        status = STATUS_TEXT;
                    }
                    break;
                case STATUS_BATCH:
                    if((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || c == '_'){ // a-zA-Z0-9_
                        currentNode.content.append(c);
                    }else{
                        nodes.add(currentNode);
                        currentNode = new TextNode();
                        currentNode.content.append(c);
                        status = STATUS_TEXT;
                    }
                    break;
            }
        }

        if(currentNode.content.length() > 0){ //最后一部分
            nodes.add(currentNode);
        }

        StringBuilder sqlBuilder = new StringBuilder();
        for(Node node: nodes){
            if(node instanceof TextNode){
                sqlBuilder.append(node.content);
            }else if(node instanceof VarNode){ //处理Var
                VarNode n = (VarNode) node;
                sqlBuilder.append("?");
                r.paramOrder.add(n.content.toString());
            }else if(node instanceof MethodNode){ //处理Method
                MethodNode n = (MethodNode) node;
                sqlBuilder.append("?");
                r.paramOrder.add("#"+n.content.toString());
            }else if(node instanceof MacroNode){ //处理Macro
                MacroNode n = (MacroNode) node;
                Object result = ExpressionUtil.get(context.params, n.content.toString());
                if(result == null){
                    throw new RuntimeException(String.format("SQLTemplate[%s] Macro[%s] cannot be null. Did you forget to @SQLParam?", sqlTemplate, n.content));
                }
                sqlBuilder.append(result.toString());
            }else if(node instanceof BatchNode){ //处理Batch
                BatchNode n = (BatchNode) node;

                Object result = ExpressionUtil.get(context.params, n.content.toString());
                List params = (List) result;
                if(result == null){
                    throw new RuntimeException(String.format("SQLTemplate[%s] Macro[%s] cannot be null. Did you forget to @SQLParam?", sqlTemplate, n.content));
                }
                List<String> placeholders = new ArrayList(params.size());
                for(Object p: params){
                    placeholders.add("?");
                }
                r.paramOrder.add(n.content.toString());
                sqlBuilder.append(StringUtils.join(placeholders, ','));
            }
        }

        if(context.cnd != null){
            context.cnd.buildSql(context, sqlBuilder);
        }

        if(context.pageInfo != null){
            sqlBuilder.append(String.format(" LIMIT %s, %s", context.pageInfo.getOffset(), context.pageInfo.getPageSize()));
        }

        r.sql = sqlBuilder.toString();

        if(XDAO.debug){
            Log.info("[SQL Template]" + context.sqlTemplate);
            Log.info("[SQL]" + r.sql);
        }

		return r;
	}

    /**
     * 解析结果
     */
	public static class Result{

        /**
         * 可以运行的SQL语句
         */
		public String sql;

        /**
         * 参数的顺序，用于后面PreparedStatement设置参数
         */
		public List<String> paramOrder = new ArrayList();
	}


}
